// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Shape2DCluster.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Microsoft.Robotics.Vision
{
    using System;
    using System.Collections.Generic;

    using Microsoft.Robotics.Numerics;
    using Microsoft.Robotics.Tracing;

    /// <summary>
    /// Represents a simple cluster of 2D shapes
    /// </summary>
    public class Shape2DCluster : IShape2D
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Shape2DCluster" /> class
        /// </summary>
        /// <param name="shape">First shape component</param>
        public Shape2DCluster(IShape2D shape)
        {
            this.Components = new List<IShape2D>() { shape };
            this.CenterX = shape.CenterX;
            this.CenterY = shape.CenterY;
            this.Confidence = shape.Confidence;
            this.Size1D = shape.Size1D;
        }

        /// <summary>
        /// Gets center of cluster - X
        /// </summary>
        public double CenterX { get; private set; }

        /// <summary>
        /// Gets center of cluster Y
        /// </summary>
        public double CenterY { get; private set; }

        /// <summary>
        /// Gets confidence for cluster
        /// </summary>
        public double Confidence { get; private set; }

        /// <summary>
        /// Gets cluster extent
        /// </summary>
        public double Size1D { get; private set; }

        /// <summary>
        /// Gets the cluster components
        /// </summary>
        public List<IShape2D> Components { get; private set; }

        /// <summary>
        /// Simple nearest neighbor type clustering of shapes
        /// </summary>
        /// <param name="shapes">List of shapes to cluster</param>
        /// <returns>cluster list</returns>
        public static IEnumerable<Shape2DCluster> Cluster(IEnumerable<IShape2D> shapes)
        {
            List<Shape2DCluster> clusters = new List<Shape2DCluster>();

            foreach (IShape2D s in shapes)
            {
                if (clusters.Count == 0)
                {
                    clusters.Add(new Shape2DCluster(s));
                }
                else
                {
                    bool found = false;

                    foreach (Shape2DCluster c in clusters)
                    {
                        if (ImageOperations2D.L2Distance(s.CenterX, s.CenterY, c.CenterX, c.CenterY) < Math.Max(c.Size1D, s.Size1D))
                        {
                            c.Add(s);
                            found = true;
                            break;
                        }
                    }

                    if (found == false)
                    {
                        clusters.Add(new Shape2DCluster(s));
                    }
                }
            }

            return clusters;
        }

        /// <summary>
        /// Add a shape to the cluster
        /// </summary>
        /// <param name="shape">Shape to add</param>
        public void Add(IShape2D shape)
        {
            this.CenterX = (this.CenterX * this.Components.Count + shape.CenterX) / (this.Components.Count + 1);
            this.CenterY = (this.CenterY * this.Components.Count + shape.CenterY) / (this.Components.Count + 1);
            double d = ImageOperations2D.L2Distance(this.CenterX, this.CenterY, shape.CenterX, shape.CenterY);
            this.Size1D = Math.Max(this.Size1D, d);
            this.Confidence = Math.Max(this.Confidence, shape.Confidence);
            this.Components.Add(shape);
        }
    }
}
